﻿#include <glm/ext.hpp>
#include <glm/glm.hpp>
#include <gtest/gtest.h>
#include <xy/geom/vec.h>

using namespace xy;
using namespace xy::geom;
using namespace xy::math;

// 测试 vec3
TEST(TestGeomVec, BasisFunctions)
{
    vec3 P{1.2, 3.4, 5.6};
    EXPECT_FLOAT_EQ(P.x(), 1.2);
    EXPECT_FLOAT_EQ(P.y(), 3.4);
    EXPECT_FLOAT_EQ(P.z(), 5.6);
    EXPECT_EQ(P, vec3(1.2, 3.4, 5.6));
    vec3 Q(1.2, 3.4, 5.6);

    vec3 P2{1.1, 3.3, 5.5};
    EXPECT_TRUE((P + P2) == vec3(2.3, 6.7, 11.1));
    EXPECT_TRUE((P - P2) == vec3(0.1, 0.1, 0.1));
    EXPECT_TRUE((P * 2) == vec3(2.4, 6.8, 11.2));
    EXPECT_TRUE((P / 2) == vec3(0.6, 1.7, 2.8));

    P = vec3{1.0, 2.0, 3.0};
    P2 = vec3{4.0, 5.0, 6.0};
    EXPECT_FLOAT_EQ(dot(P, P2), 32);
    EXPECT_TRUE(cross(P, P2) == vec3(-3.0, 6.0, -3.0));
    EXPECT_FLOAT_EQ(P.modulus(), sqrt(14));
    EXPECT_TRUE(P.normalized() == (vec3(1.0, 2.0, 3.0) / sqrt(14)));

    P.normalize();
    EXPECT_EQ(P, (vec3(1.0, 2.0, 3.0) / sqrt(14)));

    P.x() = 5;
    P.y() = 6;
    P.z() = 7;
    EXPECT_EQ(P, vec3(5.0, 6.0, 7.0));

    P += P2;
    EXPECT_EQ(P, vec3(9.0, 11.0, 13.0));

    P -= P2;
    EXPECT_EQ(P, vec3(5.0, 6.0, 7.0));

    P = vec3{1.0, 2.0, 3.0};
    P2 = vec3{4.0, 5.0, 6.0};
    P = cross(P, P2);
    EXPECT_EQ(P, vec3(-3.0, 6.0, -3.0));

    P *= 2;
    EXPECT_EQ(P, vec3(-6.0, 12.0, -6.0));

    P /= 2;
    EXPECT_EQ(P, vec3(-3.0, 6.0, -3.0));

    const double *data = P.data();
    EXPECT_FLOAT_EQ(data[0], -3);
    EXPECT_FLOAT_EQ(data[1], 6);
    EXPECT_FLOAT_EQ(data[2], -3);

    double *data2 = P.data();
    data2[0] = 1;
    data2[1] = 2;
    data2[2] = 3;
    EXPECT_EQ(P, vec3(1.0, 2.0, 3.0));
}

TEST(TestGeomVec, mat4x4)
{
    // 测试初始化、拷贝构造、移动构造、赋值
    {
        mat4x4f m1;
        m1 = mat4x4f::identity();
        mat4x4f m2(m1);
        mat4x4f m3(std::move(m2));
        mat4x4f m4 = m3;
        EXPECT_TRUE(m1 == m4);
    }
}

// 测试基本内联函数
TEST(TestGeomVec, Inline)
{
    std::mt19937 generator((unsigned int)time(nullptr));      // 使用 time(nullptr) 作为随机数生成器的种子
    std::uniform_real_distribution<double> uniform(0, 100.0); // 在 [0,1] 区间内的均匀分布

    // EXPECT_EQ(combine(vec3(1.0, 2.0, 3.0), vec3(4.0, 5.0, 6.0), 1.5, 2.5), vec3(2.875, 3.875, 4.875));

    EXPECT_EQ(homogeneous_to_cartesian(vec4(1.0, 2.0, 3.0, 2.0)), vec3(0.5, 1.0, 1.5));
    EXPECT_EQ(cartesian_to_homogeneous(vec3(0.5, 1.0, 1.5)), vec4(0.5, 1.0, 1.5, 1.0));

    {
        vec2 v1(5.0, 3.0);
        EXPECT_LE(dot(orthogonal_vec(v1), v1), numeric::distance);
    }

    {
        vec3 v1(5.0, 3.0, 6.0);
        vec3 v2(0.0, 3.0, 6.0);
        vec3 v3(5.0, 0.0, 6.0);
        vec3 v4(5.0, 3.0, 0.0);
        vec3 v5(5.0, 0.0, 0.0);
        vec3 v6(0.0, 3.0, 0.0);
        vec3 v7(0.0, 0.0, 6.0);
        vec3 v8(0.0, 0.0, 0.0);

        EXPECT_TRUE(dot(orthogonal_vec(v1), v1) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v2), v2) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v3), v3) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v4), v4) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v5), v5) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v6), v6) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v7), v7) < numeric::distance);
        EXPECT_TRUE(dot(orthogonal_vec(v8), v8) < numeric::distance);
    }

    {
        std::vector<vec2> pts(3);
        for (int i = 0; i < 3; i++)
            pts[i] = vec2{uniform(generator), uniform(generator)};
        vec2 p = (pts[0] + pts[1] + pts[2]) / 3;
        auto coords = barycentric(p, pts.data());
        EXPECT_EQ(coords, vec3(1.0 / 3, 1.0 / 3, 1.0 / 3));
    }

    {
        std::vector<vec3> pts(3);
        for (int i = 0; i < 3; i++)
            pts[i] = vec3{uniform(generator), uniform(generator), uniform(generator)};
        vec3 p = (pts[0] + pts[1] + pts[2]) / 3;
        auto coords = barycentric(p, pts.data());
        EXPECT_EQ(coords, vec3(1.0 / 3, 1.0 / 3, 1.0 / 3));
    }

    // 求逆
    {
        mat4x4f A;
        std::vector<float> data = {1, 4, 7, 0, 2, 5, 8, 0, 3, 6, 10, 0, 0, 0, 0, 1};
        std::copy(data.begin(), data.end(), A.data());
        mat4x4f B = invert(A).value();

        std::vector<float> rdata = {-2.0 / 3, -2.0 / 3, 1.0, 0.0, -4.0 / 3, 11.0 / 3, -2.0, 0.0,
                                    1.0,      -2.0,     1.0, 0.0, 0.0,      0.0,      0.0,  1.0};
        mat4x4f R;
        std::copy(rdata.begin(), rdata.end(), R.data());
        EXPECT_LT((B - R).modulus(), numeric::distance * 1e3);
    }

    // 正交投影
    {
        auto P = ortho<float>(-8, 9, -3, 2, 5, 100);
        mat4x4f Q;
        std::vector<float> data = {
            0.117647, 0, 0, -0.0588235, 0, 0.4, 0, 0.2, 0, 0, -0.0210526, -1.10526, 0, 0, 0, 1,
        };
        std::copy(data.begin(), data.end(), Q.data());
        EXPECT_LT((P - Q).modulus(), numeric::distance * 1e3);
    }

    // 透视变换
    {
        auto P = perspective<float>(65, 1.8, 0.7, 250);
        mat4x4f Q;
        std::vector<float> data = {
            0.872048, 0, 0, 0, 0, 1.56969, 0, 0, 0, 0, -1.00562, -1.40393, 0, 0, -1, 0,
        };
        std::copy(data.begin(), data.end(), Q.data());
        EXPECT_LT((P - Q).modulus(), numeric::distance * 1e3);
    }

    // lookAt
    {
        auto P = look_at<float>(vec3f{1.0f, 2.0f, 2.0f}, vec3f{4.0f, 5.0f, 6.0f}, vec3f{7.0f, 8.0f, 9.0f});
        mat4x4f Q;
        std::vector<float> data = {-0.845154, 0.169031,  0.507093,  -0.507093, -0.144943, 0.840668,
                                   -0.521794, -0.492805, -0.514496, -0.514496, -0.685994, 2.91548,
                                   0,         0,         0,         1};
        std::copy(data.begin(), data.end(), Q.data());

        auto P2 = glm::lookAt(glm::vec3{1.0f, 2.0f, 2.0f}, glm::vec3{4.0f, 5.0f, 6.0f}, glm::vec3{7.0f, 8.0f, 9.0f});
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                std::cout << P2[i][j] << " ";
            }
            std::cout << std::endl;
        }

        EXPECT_LT((P - Q).modulus(), numeric::distance * 1e3);

        P2 = glm::inverse(P2);
        Q = invert(Q).value();

        std::cout << Q << std::endl;

        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                std::cout << P2[i][j] << " ";
            }
            std::cout << std::endl;
        }
    }
}
